﻿using wwl.Domain.Entities.Common;

namespace wwl.Domain.Entities.Rbac
{
  /// <summary>
  /// 用户聚合根（DDD 核心领域实体）
  /// 属于权限模块（Rbac）的核心聚合根，承载用户身份认证与权限分配的核心业务状态
  /// 聚合边界：用户基础信息 + 关联角色集合 + 认证凭据，构成完整的用户身份上下文
  /// 设计对齐 Camunda 的 act_id_user 表结构，确保工作流引擎与业务系统用户模型一致性
  /// </summary>
  public class User : BaseEntity, ITenantAware
  {
    /// <summary>
    /// 登录用户名（领域核心业务标识）
    /// </summary>
    /// <remarks>
    /// 1. 遵循 DDD"实体业务唯一性"原则：用户名是用户身份的核心业务标识，区别于技术主键 Id，用于登录认证和业务识别。
    /// 2. 业务规则内嵌：用户名全局唯一、不可为空、长度受限（≤64字符），该规则通过领域服务（如 UserService）和仓储接口（如 IUserRepository.UsernameExistsAsync）保障。
    /// 3. 多租户隔离：同一用户名在不同租户下可重复，通过 TenantId 实现租户级用户名唯一性约束。
    /// 4. 空字符串默认值：避免 null 引用异常，确保实体状态初始化完整，符合 DDD"实体自包含"设计原则。
    /// </remarks>
    public string Username { get; set; } = string.Empty;

    /// <summary>
    /// 密码哈希值（安全凭据存储）
    /// </summary>
    /// <remarks>
    /// 1. 安全设计原则：绝不存储明文密码，采用 BCrypt 等强哈希算法处理，确保用户凭据安全。
    /// 2. 业务规则内嵌：密码强度校验（长度、复杂度）在应用层完成，哈希值在此存储，体现"安全与存储分离"架构思想。
    /// 3. 可空设计：支持外部认证（如 LDAP、OAuth）场景，此时密码哈希可为空，体现领域模型的扩展性。
    /// 4. 长度预留：255字符长度支持多种哈希算法输出，为未来算法升级预留空间。
    /// </remarks>
    public string PasswordHash { get; set; } = string.Empty;

    /// <summary>
    /// 用户全名（业务展示标识）
    /// </summary>
    /// <remarks>
    /// 1. 业务语义明确：区别于登录用户名，全名用于界面展示、报表输出等业务场景，提升用户体验。
    /// 2. 国际化支持：128字符长度支持多语言姓名格式，适应全球化业务需求。
    /// 3. 可空设计：允许用户注册时暂不填写全名，支持分步完善用户信息的业务场景。
    /// 4. 与 Camunda 对齐：对应 act_id_user 表的 FIRST_ 和 LAST_ 字段合并，简化业务模型。
    /// </remarks>
    public string FullName { get; set; } = string.Empty;

    /// <summary>
    /// 电子邮箱（多渠道身份标识）
    /// </summary>
    /// <remarks>
    /// 1. 多用途设计：既作为登录凭证（邮箱登录），也用于系统通知、密码重置等业务场景。
    /// 2. 格式校验：邮箱格式校验在应用层完成，领域层仅存储已验证的邮箱地址。
    /// 3. 唯一性约束：支持全局或租户内邮箱唯一性，具体约束策略由业务规则决定。
    /// 4. 长度预留：128字符支持长域名邮箱地址，适应企业邮箱等复杂场景。
    /// </remarks>
    public string Email { get; set; } = string.Empty;

    /// <summary>
    /// 联系电话（多渠道身份标识）
    /// </summary>
    /// <remarks>
    /// 1. 可选联系方式：支持手机号验证、短信通知等业务场景，但不作为核心必填项。
    /// 2. 格式标准化：电话号码的格式化、验证在应用层处理，领域层存储标准化后的号码。
    /// 3. 国际化支持：32字符长度支持国际电话号码格式，包括国家代码、区号等。
    /// 4. 隐私保护：电话号码属于敏感信息，访问需权限控制，体现领域模型的安全意识。
    /// </remarks>
    public string? Phone { get; set; }

    /// <summary>
    /// 用户状态（领域状态枚举，驱动业务流程）
    /// </summary>
    /// <remarks>
    /// 1. 状态驱动设计：用户状态直接影响系统行为（如登录权限、数据访问），体现"状态即业务规则"的 DDD 思想。
    /// 2. 三态设计：
    ///    - ENABLED：正常状态，可登录和使用系统功能
    ///    - DISABLED：管理员手动禁用，保留数据但限制登录
    ///    - LOCKED：安全机制自动锁定（如密码错误次数超限），需管理员解锁
    /// 3. 状态流转控制：状态变更通过领域方法（Enable/Disable/Lock）封装，确保状态转换符合业务规则。
    /// 4. 默认启用：新用户创建后默认可用状态，减少管理员干预，提升用户体验。
    /// </remarks>
    public UserStatus Status { get; set; } = UserStatus.ENABLED;

    /// <summary>
    /// 租户标识（多租户数据隔离）
    /// </summary>
    /// <remarks>
    /// 1. 多租户架构核心：通过 TenantId 实现数据层面的租户隔离，确保不同租户用户数据完全独立。
    /// 2. 业务规则关联：用户的操作权限、数据访问范围均受租户边界限制，体现"租户即安全边界"设计原则。
    /// 3. 可空设计：支持系统级管理员用户（TenantId=null），具备跨租户管理权限的特殊场景。
    /// 4. 外键关联：与 Tenant 聚合根建立逻辑关联，但不强制数据库外键约束，保持领域模型纯洁性。
    /// </remarks>
    public string? TenantId { get; set; }

    /// <summary>
    /// 用户角色关联集合（聚合内导航属性）
    /// </summary>
    /// <remarks>
    /// 1. 聚合内包含：用户与角色的关联关系作为用户聚合的一部分，确保用户权限数据的完整性和一致性。
    /// 2. 延迟加载：通过 EF Core 的虚拟属性实现按需加载，避免不必要的数据查询，提升性能。
    /// 3. 业务完整性：通过该导航属性可完整追溯用户的权限来源，支持复杂的权限分析和审计需求。
    /// 4. 设计对齐：对应 Camunda 的 act_id_membership 表概念，但在此作为聚合内部分实现更紧密的关联。
    /// </remarks>
    public virtual ICollection<UserRole> UserRoles { get; set; } = new List<UserRole>();

    /// <summary>
    /// 领域行为：启用用户账户
    /// </summary>
    /// <remarks>
    /// 1. 业务规则封装：启用操作包含状态校验（避免重复启用）和更新时间同步，体现"实体自包含业务逻辑"的 DDD 原则。
    /// 2. 状态驱动：从 DISABLED 或 LOCKED 状态转换为 ENABLED，恢复用户所有系统权限。
    /// 3. 异常处理：通过 InvalidOperationException 明确业务规则违反场景，为调用方提供清晰的错误信息。
    /// 4. 审计追踪：自动更新 UpdatedTime，为管理员操作审计提供完整的时间线索。
    /// </remarks>
    /// <exception cref="InvalidOperationException">当用户已处于启用状态时抛出</exception>
    public void Enable()
    {
      if (Status == UserStatus.ENABLED)
        throw new InvalidOperationException("用户已处于启用状态，无需重复操作");

      Status = UserStatus.ENABLED;
      UpdatedTime = DateTime.UtcNow;
    }

    /// <summary>
    /// 领域行为：禁用用户账户
    /// </summary>
    /// <remarks>
    /// 1. 管理性操作：通常由管理员手动执行，用于临时限制用户访问权限的场景。
    /// 2. 状态隔离：与 LOCKED 状态区分，DISABLED 是管理行为，LOCKED 是安全机制自动触发。
    /// 3. 数据保留：禁用状态保留用户所有数据，仅限制登录和操作权限，支持后续重新启用。
    /// 4. 业务连续性：适用于员工休假、权限临时收回等需要保留用户上下文的管理场景。
    /// </remarks>
    /// <exception cref="InvalidOperationException">当用户已处于禁用状态时抛出</exception>
    public void Disable()
    {
      if (Status == UserStatus.DISABLED)
        throw new InvalidOperationException("用户已处于禁用状态，无需重复操作");

      Status = UserStatus.DISABLED;
      UpdatedTime = DateTime.UtcNow;
    }

    /// <summary>
    /// 领域行为：锁定用户账户
    /// </summary>
    /// <remarks>
    /// 1. 安全机制：通常由系统自动触发（如密码连续错误次数超限），防止暴力破解攻击。
    /// 2. 自动恢复：可与时间策略结合，支持自动解锁或需要管理员手动解锁的不同安全策略。
    /// 3. 审计要求：锁定操作应记录详细原因和上下文，支持安全事件分析和追溯。
    /// 4. 用户体验：在安全性和便利性之间平衡，避免过度锁定影响正常业务操作。
    /// </remarks>
    /// <exception cref="InvalidOperationException">当用户已处于锁定状态时抛出</exception>
    public void Lock()
    {
      if (Status == UserStatus.LOCKED)
        throw new InvalidOperationException("用户已处于锁定状态，无需重复操作");

      Status = UserStatus.LOCKED;
      UpdatedTime = DateTime.UtcNow;
    }
  }

  /// <summary>
  /// 用户状态枚举（DDD 领域枚举，定义用户生命周期状态）
  /// </summary>
  /// <remarks>
  /// 1. 业务语义化：枚举值采用清晰的业务术语（ENABLED/DISABLED/LOCKED），避免技术性编码（如 0/1/2），提升代码可读性。
  /// 2. 状态完整性：覆盖用户账户的所有可能状态，每个状态都有明确的业务含义和转换规则。
  /// 3. 扩展性设计：枚举定义与具体实现解耦，支持未来新增状态（如 EXPIRED）而不影响现有业务逻辑。
  /// 4. 数据库映射：通过 EF Core 的值转换器映射为字符串存储，确保数据库层面的可读性和查询便利性。
  /// </remarks>
  public enum UserStatus
  {
    /// <summary>
    /// 启用状态：用户可正常登录系统，使用所有被授权的功能和数据
    /// </summary>
    ENABLED,

    /// <summary>
    /// 禁用状态：管理员手动禁用，用户无法登录系统，但保留所有数据和权限配置
    /// </summary>
    DISABLED,

    /// <summary>
    /// 锁定状态：安全机制自动锁定（如密码错误超限），需管理员干预解锁才能恢复正常
    /// </summary>
    LOCKED
  }
}